package com.hivekion.workflow.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.hivekion.common.exception.BusinessException;
import com.hivekion.workflow.domain.vo.ProcessModelInputVo;
import com.hivekion.workflow.domain.vo.ProcessModelUpdateInputVo;
import com.hivekion.workflow.service.ProcessDesignService;
import com.hivekion.workflow.utils.BpmnConverterUtil;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.UserTask;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class ProcessDesignServiceImpl implements ProcessDesignService {
      @Autowired
      private RepositoryService repositoryService;
      @Autowired
      private ObjectMapper objectMapper;

      @Override
      public String deployModel(String modelId) throws Exception {
	    // 获取模型
	    Model modelData = repositoryService.getModel(modelId);
	    byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());
	    if (null == bytes) {
		  return "模型数据为空，请先设计流程并成功保存，再进行发布。";
	    }
	    JsonNode modelNode = objectMapper.readTree(bytes);
	    BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
	    if (model.getProcesses().size() == 0) {
		  return "数据模型不符合要求，请至少设计一条主线程流。";
	    }
	    byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
	    // 发布流程
	    String processName = modelData.getName() + ".bpmn20.xml";
	    Deployment deployment =
		    repositoryService
			    .createDeployment()
			    .name(modelData.getName())
			    .addString(processName, new String(bpmnBytes, "UTF-8"))
			    .deploy();
	    modelData.setDeploymentId(deployment.getId());
	    repositoryService.saveModel(modelData);
	    return null;
      }

      @Override
      public List<FlowElement> getFlowElements(String processId) {
	    BpmnModel model = repositoryService.getBpmnModel(processId);
	    if (model != null) {
		  Collection<FlowElement> flowElements = model.getMainProcess().getFlowElements();
		  return flowElements.stream()
			  .filter(item -> item.getClass() == UserTask.class)
			  .collect(Collectors.toList());
	    }
	    return null;
      }

      @Override
      public void createModel(ProcessModelInputVo inputVo) {
	    Model model = repositoryService.newModel(); // 初始化一个空模型
	    BeanUtils.copyProperties(inputVo, model);
	    model.setMetaInfo(inputVo.toString());
	    repositoryService.saveModel(model);
	    String modelId = model.getId();
	    // 完善ModelEditorSource
	    ObjectNode editorNode = objectMapper.createObjectNode();
	    editorNode.put("id", "canvas");
	    editorNode.put("resourceId", "canvas");
	    ObjectNode stencilSetNode = objectMapper.createObjectNode();
	    stencilSetNode.put("namespace", "http://activiti.org/bpmn");
	    editorNode.put("stencilset", stencilSetNode);
	    try {
		  repositoryService.addModelEditorSource(
			  modelId, editorNode.toString().getBytes("utf-8"));
	    } catch (UnsupportedEncodingException e) {
		  throw new BusinessException(500, "异常");
	    }
      }

      @Override
      public JSONObject getEditorXml(String modelId) {
	    JSONObject jsonObject = null;
	    Model model = repositoryService.getModel(modelId);
	    if (model != null) {
		  try {
			if (StringUtils.isNotEmpty(model.getMetaInfo())) {
			      jsonObject = JSON.parseObject(model.getMetaInfo());
			} else {
			      jsonObject = new JSONObject();
			      jsonObject.put("name", model.getName());
			}
			jsonObject.put("id", model.getId());
			JSONObject editorJsonNode =
				JSON.parseObject(
					new String(repositoryService.getModelEditorSource(model.getId())));
			// 将json流程转为标准xml流程图
			String bpmnXml =
				BpmnConverterUtil.converterJsonToWebXml(editorJsonNode.toJSONString());
			jsonObject.put("bpmnXml", bpmnXml);
		  } catch (Exception e) {
			// LOG.error("创建model的json串失败", e);
			throw new ActivitiException("无法读取model信息", e);
		  }
	    } else {
		  // LOG.error("创建model的json串失败[{}]", modelId);
		  throw new ActivitiException("未找到对应模型信息");
	    }
	    return jsonObject;
      }

      @Override
      public void saveModelXml(ProcessModelUpdateInputVo inputVo) throws IOException {
	    Model model = repositoryService.getModel(inputVo.getModelId());
	    if (model == null) {
		  throw new BusinessException(500, "未找到模型");
	    }
	    model.setVersion(model.getVersion() + 1); // 版本
	    ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo()); // 获取模型信息
	    if (modelJson != null) {
		  // 获取value第一个元素
		  modelJson.put("name", model.getName());
		  modelJson.put("description", modelJson.get("description"));
		  model.setMetaInfo(modelJson.toString());
	    }
	    repositoryService.saveModel(model);
	    String bpmnXml = BpmnConverterUtil.converterXmlToJson(inputVo.getBpmn_xml()).toString();
	    repositoryService.addModelEditorSource(model.getId(), bpmnXml.getBytes("utf-8"));
	    repositoryService.addModelEditorSourceExtra(
		    model.getId(), inputVo.getSvg_xml().getBytes("utf-8"));
      }
}
